﻿using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Cofe.Core.Actions;
using Cofe.Core.Property;
using Cofe.Core.Validater;
using Cofe.Web;
using Newtonsoft.Json.Linq;

namespace Cofe.Core
{
    public class ParameterDic : Dictionary<string, object>
    {
        public static ParameterDic Empty = new ParameterDic();

        public ParameterDic()
            : base(StringComparer.CurrentCultureIgnoreCase)
        {

        }

        public static ParameterDic FromNameValueCollection(NameValueCollection col, string[] paramToFetch)
        {
            ParameterDic retVal = new ParameterDic();

            foreach (var key in col.AllKeys)
            {
                if (paramToFetch.Contains(key, StringComparer.CurrentCultureIgnoreCase))
                {
                    retVal.Add(key, col[key]);
                }
            }
            return retVal;
        }

        public static ParameterDic FromJObject(JObject data, Func<string, bool> filter)
        {
            ParameterDic retVal = new ParameterDic();

            IDictionary<string, JToken> dataDic = (IDictionary<string, JToken>)data;
            foreach (var tokenPairs in data)
            {
                if (filter(tokenPairs.Key))
                {
                    switch (tokenPairs.Value.Type)
                    {
                        case JTokenType.String: retVal.Add(tokenPairs.Key, tokenPairs.Value.Value<string>()); break;
                        case JTokenType.Guid: retVal.Add(tokenPairs.Key, tokenPairs.Value.Value<Guid>()); break;
                    }
                }
            }

            return retVal;
        }

        public static ParameterDic FromJObject(JObject data, params string[] parameters)
        {
            return FromJObject(data, (k) => parameters.Contains(k));
        }


        public void AddOrUpdate(string key, object value)
        {
            if (this.ContainsKey(key))
                this[key] = value;
            else this.Add(key, value);
        }

        public void LoadParameterDic(ParameterDic pd)
        {
            this.Clear();
            foreach (var pp in pd)
                this.Add(pp.Key, pp.Value);
        }

        public ParameterDic Clone()
        {
            ParameterDic retVal = new ParameterDic();
            retVal.LoadParameterDic(this);
            return retVal;
        }

        public IPropertyHost PropertyHost
        {
            get { return this.ContainsKey("PropertyHost") && this["PropertyHost"] is IPropertyHost ? this["PropertyHost"] as IPropertyHost : null; }
            set { if (this.ContainsKey("PropertyHost")) this["PropertyHost"] = value; else this.Add("PropertyHost", value); }
        }

        public string[] TagNamesAsArray
        {
            get { return TagNames.Split(new char[] { ',', ' ' }, StringSplitOptions.RemoveEmptyEntries); }
            set { TagNames = String.Join(",", value); }
        }

        public string TagNames
        {
            get { return this.ContainsKey("TagNames") && this["TagNames"] is string ? this["TagNames"] as string : ""; }
            set { if (this.ContainsKey("TagNames")) this["TagNames"] = value; else this.Add("TagNames", value); }
        }

        public bool IsHandled
        {
            get { return this.ContainsKey("Handled") && true.Equals(this["Handled"]); }
            set { if (this.ContainsKey("Handled")) this["Handled"] = value; else this.Add("Handled", value); }
        }

        public bool EnableCache
        {
            get { return !this.ContainsKey("EnableCache") || true.Equals(this["EnableCache"]); }
            set { if (this.ContainsKey("EnableCache")) this["EnableCache"] = value; else this.Add("EnableCache", value); }
        }

        public bool Recursive
        {
            get { return this.ContainsKey("Recursive") && true.Equals(this["Recursive"]); }
            set { if (this.ContainsKey("Recursive")) this["Recursive"] = value; else this.Add("Recursive", value); }
        }

        public string Sender
        {
            get { return this.ContainsKey("Sender") ? (string)this["Sender"] : null; }
            set { if (this.ContainsKey("Sender")) this["Sender"] = value; else this.Add("Sender", value); }
        }

        public bool UpdateCurrentEntry
        {
            get { return !this.ContainsKey("UpdateCurrentEntry") || true.Equals(this["UpdateCurrentEntry"]); }
            set { if (this.ContainsKey("UpdateCurrentEntry")) this["UpdateCurrentEntry"] = value; else this.Add("UpdateCurrentEntry", value); }
        }

        public bool UpdateSubEntries
        {
            get { return this.ContainsKey("UpdateSubEntries") && true.Equals(this["UpdateSubEntries"]); }
            set { if (this.ContainsKey("UpdateSubEntries")) this["UpdateSubEntries"] = value; else this.Add("UpdateSubEntries", value); }
        }

        public IEntrySerializerHelper[] EntrySerializerHelpers
        {
            get { return this.ContainsKey("EntrySerializerHelpers") ?  (IEntrySerializerHelper[])this["EntrySerializerHelpers"] : null; }
            set { if (this.ContainsKey("EntrySerializerHelpers")) this["EntrySerializerHelpers"] = value; else this.Add("EntrySerializerHelpers", value); }
        }


        /// <summary>
        /// Most exception is throw directly, if not, it will set the Error property, which will be thrown 
        /// in PropertyInvoker.ensureNoError() method.
        /// </summary>
        public Exception Error
        {
            get { return this.ContainsKey("Error") ? this["Error"] as Exception : null; }
            set { if (this.ContainsKey("Error")) this["Error"] = value; else this.Add("Error", value); }
        }

        public CancellationToken CancellationToken
        {
            get { return this.ContainsKey("CancellationToken") ? (CancellationToken)this["CancellationToken"] : CancellationToken.None; }
            set { if (this.ContainsKey("CancellationToken")) this["CancellationToken"] = value; else this.Add("CancellationToken", value); }
        }

        public IProgress<ProgressEventArgs> Progress
        {
            get { return this.ContainsKey("Progress") ? this["Progress"] as IProgress<EventArgs> : null; }
            set { if (this.ContainsKey("Progress")) this["Progress"] = value; else this.Add("Progress", value); }
        }
    }
}
